use serde::{Deserialize, Deserializer};
use std::fmt::{Display, Formatter};

/// 数据库连接配置
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DatabaseConfig {

    /// 如果指定了url
    /// 则忽略 protocol、host、port、username、password、schema、database的值
    url: Option<String>,
    /// 数据库类型
    #[serde(default)]
    protocol: Protocol,
    /// ip
    host: Option<String>,
    /// 端口
    port: Option<u16>,
    /// 用户名
    username: Option<String>,
    /// 密码
    password: Option<String>,
    /// Postgres特有
    schema: Option<String>,
    /// 数据库名
    database: Option<String>,
    /// 连接池最大连接数
    max_connections: Option<u32>,
    /// 连接池最小连接数
    #[serde(default)]
    min_connections: Option<u32>,
    /// 连接超时时间
    connect_timeout: Option<u16>,
    /// 连接最大空闲时间
    idle_timeout: Option<u16>,
    /// 启用 SQLx 语句日志记录
    #[serde(default)]
    enable_logging: bool,
}

impl Default for DatabaseConfig {
    fn default() -> Self {
        Self{
            url: None,
            protocol: Default::default(),
            host: None,
            port: None,
            username: None,
            password: None,
            schema: None,
            database: None,
            max_connections: None,
            min_connections: None,
            connect_timeout: None,
            idle_timeout: None,
            enable_logging: false,
        }
    }
}

impl DatabaseConfig {
    pub fn url(&self) -> Option<&String> {
        self.url.as_ref()
    }
    pub fn protocol(&self) -> &Protocol {
        &self.protocol
    }

    pub fn host(&self) -> &str {
        self.host.as_deref().unwrap_or("localhost")
    }

    pub fn port(&self) -> u16 {
        self.port.unwrap_or(3306)
    }

    pub fn username(&self) -> &str {
        self.username.as_deref().unwrap_or("root")
    }

    pub fn password(&self) -> &str {
        self.password.as_deref().unwrap_or("root")
    }

    pub fn schema(&self) -> &str {
        self.schema.as_deref().unwrap_or("my_schema")
    }

    pub fn database(&self) -> &str {
        self.database.as_deref().unwrap_or("test")
    }

    pub fn max_connections(&self) -> u32 {
        self.max_connections.unwrap_or(10)
    }

    pub fn min_connections(&self) -> u32 {
        self.min_connections.unwrap_or(0)
    }

    pub fn connect_timeout(&self) -> u16 {
        self.connect_timeout.unwrap_or(30)
    }

    pub fn idle_timeout(&self) -> u16 {
        self.idle_timeout.unwrap_or(600)
    }

    pub fn enable_logging(&self) -> bool {
        self.enable_logging
    }
}

pub enum Protocol {
    Mysql,
    Postgres,
    Sqlite,
}

impl Default for Protocol {
    fn default() -> Self {
        Protocol::Mysql
    }
}

impl Display for Protocol {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Protocol::Mysql => write!(f, "mysql"),
            Protocol::Postgres => write!(f, "postgres"),
            Protocol::Sqlite => write!(f, "sqlite"),
        }
    }
}

impl<'de> Deserialize<'de> for Protocol {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        match s.to_lowercase().as_str() {
            "mysql" => Ok(Protocol::Mysql),
            "postgres" => Ok(Protocol::Postgres),
            "sqlite" => Ok(Protocol::Sqlite),
            _ => Err(serde::de::Error::custom("不支持的数据库类型")),
        }
    }
}
